package main

import (
	"fmt"
	"sync"
	"time"
)

/*
Goroutines：
	go func(参数) {
		代码
	} (参数)
    Go 中的并发执行单位，类似于轻量级的线程。同一个程序中的所有 goroutine 共享同一个地址空间。
    Goroutine 的调度由 Go 运行时管理，用户无需手动分配线程。
    使用 go 关键字启动 Goroutine。
    Goroutine 是非阻塞的，可以高效地运行成千上万个 Goroutine。

Channel：
	var channelName chan elementType
    Go 中用于在 Goroutine 之间通信的机制。遵循先进先出原则。
    支持同步和数据共享，避免了显式的锁机制。
    使用 chan 关键字创建，通过 <- 操作符发送和接收数据。
	默认情况下，通道是不带缓冲区的。发送端发送数据，同时必须有接收端相应的接收数据。

Scheduler（调度器）: Go 的调度器基于 GMP 模型，调度器会将 Goroutine 分配到系统线程中执行，并通过 M 和 P 的配合高效管理并发。
    G：Goroutine。
    M：系统线程（Machine）。
    P：逻辑处理器（Processor）。
*/
/*
go func(c chan int) { //读写均可的channel c } (a)
go func(c <- chan int) { //只读的Channel } (a)
go func(c chan <- int) {  //只写的Channel } (a)
*/
func sayHello(c chan<- string, wg *sync.WaitGroup) {
	defer (*wg).Done()
	for i := 0; i < 5; i++ {
		// fmt.Printf("Hello %d \n", i)
		c <- fmt.Sprintf("你好 %d", i)
		time.Sleep(100 * time.Millisecond) //睡0.1秒
	}
	//关闭通道并不会丢失里面的数据，只是让读取通道数据的代码，在取出所有数据后，强制不再等待读取，并继续向下执行
	close(c)
	fmt.Println("sayHello finished")
}

func sayWorld(c chan<- int, wg *sync.WaitGroup) {
	defer (*wg).Done()
	for i := 0; i < 5; i++ {
		// fmt.Printf("World %d \n", i)
		c <- (i*10000000 + 1)
		time.Sleep(100 * time.Millisecond)
	}
	close(c)
	fmt.Println("sayWorld finished")
}

func main() {
	var chan1 chan string = make(chan string)
	var chan2 chan int = make(chan int, 5) //缓冲区大小是5个int类型
	var chan3 chan string = make(chan string, 10)
	var wg sync.WaitGroup

	/* go func() {
		for i := 0; i < 5; i++ {
			fmt.Printf("Xue %d \n", i)
			time.Sleep(100 * time.Millisecond)
		}
	}() */
	wg.Add(1)
	go sayHello(chan1, &wg)
	wg.Add(1)
	go sayWorld(chan2, &wg)
	wg.Add(1)
	go func(c1 <-chan string, c2 <-chan int, wg *sync.WaitGroup) {
		defer (*wg).Done()
		//获取通道数据
		for i := 0; i < 10; i++ {
			select {
			case str, _ := <-chan1:
				fmt.Println(str)
			case num, _ := <-chan2:
				fmt.Println(num)
			}
		}
		fmt.Println("获取通道数据 finished")
	}(chan1, chan2, &wg)
	wg.Add(1)
	go func(c chan string, wg *sync.WaitGroup) {
		defer (*wg).Done()
		time.Sleep(5000 * time.Millisecond)
		for str := range c { //for str := range c就是固定这么写，会随着c被close自动跳出循环
			fmt.Println(str)
			time.Sleep(100 * time.Millisecond)
		}
		fmt.Println("获取主线程数据 finished")
	}(chan3, &wg)

	for i := 0; i < 10; i++ { //主线程要花10秒执行完毕
		// fmt.Printf("主线程正在做事情 %d \n", i)
		chan3 <- fmt.Sprintf("主线程 %d", i)
		time.Sleep(1000 * time.Millisecond)
	}
	close(chan3)
	//判断所有线程是否全部执行完毕
	wg.Wait()
	fmt.Println("所有线程全部执行完毕")
}
